#!/bin/bash

dep_nodes=()
sorted_g1_deps=()
unique_lx_deps=()
dot_content=""

collect_deps_() {
  local initial_dep="$1"
  local initial_dep_ar=($initial_dep)
  local deps=""
  local lx_deps=""
  local queue=()
  for dep in "${initial_dep_ar[@]}"; do
    if [[ "$dep" =~ ^[f][0-9]+[a-zA-Z]+[0-9]+[v][0-9]+[.][hHpPxX]+ ]]; then
      queue+=("$dep")
    else
      lx_deps="$lx_deps $dep"
    fi
  done
  local visited=()
  dep_nodes=() # fps_file|line_num|dep|reliance for topological sort
  while [ ${#queue[@]} -gt 0 ]; do
    local dep="${queue[0]}"
    queue=("${queue[@]:1}") # inspect the first element in queue
    [[ -z "$dep" || "$dep" == "-" ]] && continue
    [[ " ${visited[*]} " =~ " ${dep} " ]] && continue
    visited+=("$dep") # f100pthdfunc0v1.hh
    local fx6a1=${dep%%[0-9]*} # f
    local fx6b1=${dep:${#fx6a1}} # 100pthdfunc0v1.hh
    local fx6a2=${fx6b1%%[a-zA-Z]*} # 100
    local fx6b2=${fx6b1:${#fx6a2}} # pthdfunc0v1.hh
    local fx6a3=${fx6b2%%[0-9]*} # pthdfunc
    local fx6b3=${fx6b2:${#fx6a3}} # 0v1.hh
    local fx6a4=${fx6b3%%[v]*} # 0
    local fx6b4=${fx6b3:${#fx6a4}+1} # 1.hh
    local fx6a5=${fx6b4%%[.]*} # 1
    local dep_sector="$fx6a2"
    local dep_name="$fx6a3"
    local dep_tier="$fx6a4"
    local dep_version="$fx6a5"
    local fps_file="m${dep_sector}.fps"
    if [[ ! -f "$fps_file" ]]; then
      echo "$0: FPS file $fps_file not found for dependency $dep." >&2
      exit 1
    fi
    local dep_entry=$(grep "^${dep_sector}|${dep_name}|FCTN|${dep_tier}+|v${dep_version}|" "$fps_file")
    [[ -z "$dep_entry" ]] && continue
    local line_num=$(grep -n "^${dep_sector}|${dep_name}|FCTN|${dep_tier}+|v${dep_version}|" "$fps_file" | cut -d':' -f1)
    deps="$deps $dep"
    local dep_reliance=$(echo "$dep_entry" | cut -d'|' -f6)
    local dep_reliance_ar=($(echo "$dep_reliance" | sed 's|,| |g'))
    local reliance_list_g1=""
    local reliance_list_lx=""
    for sub_dep in "${dep_reliance_ar[@]}"; do
      if [[ "$sub_dep" =~ ^[f][0-9]+[a-zA-Z]+[0-9]+[v][0-9]+[.][hHpPxX]+ ]]; then
        if [[ ! " ${visited[*]} " =~ " ${sub_dep} " ]]; then
          queue+=("$sub_dep")
        fi
        reliance_list_g1="$reliance_list_g1 $sub_dep"
      else
        reliance_list_lx="$reliance_list_lx $sub_dep"
        lx_deps="$lx_deps $sub_dep"
      fi
    done
    dep_nodes+=("$fps_file|$line_num|$dep|$reliance_list_g1|$reliance_list_lx")
  done
  local cycle_detected=false
  local temp_mark=()
  local perm_mark=()
  sorted_g1_deps=()
  topological_sort_() {
    local node="$1"
    if [[ " ${temp_mark[*]} " =~ " ${node} " ]]; then
      cycle_detected=true
      return
    fi
    if [[ " ${perm_mark[*]} " =~ " ${node} " ]]; then
      return
    fi
    temp_mark+=("$node")
    local node_entry=""
    for entry in "${dep_nodes[@]}"; do
      if [[ "$entry" =~ ^[^|]*\|[^|]*\|${node}\| ]]; then
        node_entry="$entry"
        break
      fi
    done
    if [[ -z "$node_entry" ]]; then
      return
    fi
    local reliance_g1=$(echo "$node_entry" | cut -d'|' -f4)
    local reliance_ar=($reliance_g1)
    for sub_dep in "${reliance_ar[@]}"; do
      topological_sort_ "$sub_dep"
    done
    temp_mark=(${temp_mark[@]/$node})
    perm_mark+=("$node")
    sorted_g1_deps+=("$node")
  }
  for dep in $deps; do
    topological_sort_ "$dep"
  done
  if [[ "$cycle_detected" == true ]]; then
    echo "$0: Dependency cycle detected." >&2
    exit 1
  fi
  # sorted_g1_deps
  local sorted_nodes=()
  for dep in "${sorted_g1_deps[@]}"; do
    for entry in "${dep_nodes[@]}"; do
      if [[ "$entry" =~ ^[^|]*\|[^|]*\|${dep}\| ]]; then
        sorted_nodes+=("$(echo "$entry" | cut -d'|' -f1,2,3)")
        break
      fi
    done
  done
  sorted_g1_deps=($(printf "%s\n" "${sorted_nodes[@]}" | sort -t'|' -k1,1 -k2,2n | cut -d'|' -f3))
  # unique_lx_deps
  unique_lx_deps=()
  local seen_lx=()
  for lx in $lx_deps; do
    if [[ "$lx" != "-" && ! " ${seen_lx[*]} " =~ " ${lx} " ]]; then
      unique_lx_deps+=("$lx")
      seen_lx+=("$lx")
    fi
  done
  echo "$0: G1 deps on ${sorted_g1_deps[*]}"
  echo "$0: LX deps on ${unique_lx_deps[*]}"
}

generate_dot_graph_() {
  local m0type="$1"
  local m0sector="$2"
  local m0name="$3"
  local m0tier="$4"
  local m0version="$5"
  local m0reliance="$6"
  # names like a3func2build.sh does
  local tier0="${m0tier//+}"
  local root_name
  local root_label
  if [[ "${m0type}" == "FCTN" ]]; then
    root_name="f${m0sector}${m0name}${tier0}${m0version}"
    root_label="${root_name}"
  else
    root_name="a${m0sector}${m0name}${tier0}${m0version}"
    root_label="${root_name}"
  fi
  dot_content="digraph \"${root_name}\" {\n"
  dot_content="${dot_content}  rankdir=BT;\n"
  dot_content="${dot_content}  node [shape=box, style=filled, fillcolor=lightblue];\n\n"
  # root node
  dot_content="${dot_content}  \"${root_name}\" [label=\"${root_label}\", fillcolor=lightgreen];\n\n"
  # dependency nodes
  for dep in "${sorted_g1_deps[@]}"; do
    local fx6a1=${dep%%[0-9]*}
    local fx6b1=${dep:${#fx6a1}}
    local fx6a2=${fx6b1%%[a-zA-Z]*}
    local fx6b2=${fx6b1:${#fx6a2}}
    local fx6a3=${fx6b2%%[0-9]*}
    local fx6b3=${fx6b2:${#fx6a3}}
    local fx6a4=${fx6b3%%[v]*}
    local fx6b4=${fx6b3:${#fx6a4}+1}
    local fx6a5=${fx6b4%%[.]*}
    local func_name="f${fx6a2}${fx6a3}${fx6a4}v${fx6a5}"
    dot_content="${dot_content}  \"${func_name}\" [label=\"${func_name}\"];\n"
  done
  for lx_dep in "${unique_lx_deps[@]}"; do
    dot_content="${dot_content}  \"${lx_dep}\" [label=\"${lx_dep}\", fillcolor=gold];\n"
  done
  dot_content="${dot_content}\n"
  # edges from root to direct dependencies
  local reliance_ar=($m0reliance)
  for dep in "${reliance_ar[@]}"; do
    if [[ "$dep" == "-" ]]; then
      continue
    fi
    if [[ "$dep" =~ ^[f][0-9]+[a-zA-Z]+[0-9]+[v][0-9]+[.][hHpPxX]+ ]]; then
      local fx6a1=${dep%%[0-9]*}
      local fx6b1=${dep:${#fx6a1}}
      local fx6a2=${fx6b1%%[a-zA-Z]*}
      local fx6b2=${fx6b1:${#fx6a2}}
      local fx6a3=${fx6b2%%[0-9]*}
      local fx6b3=${fx6b2:${#fx6a3}}
      local fx6a4=${fx6b3%%[v]*}
      local fx6b4=${fx6b3:${#fx6a4}+1}
      local fx6a5=${fx6b4%%[.]*}
      local func_name="f${fx6a2}${fx6a3}${fx6a4}v${fx6a5}"
      dot_content="${dot_content}  \"${root_name}\" -> \"${func_name}\";\n"
    else
      dot_content="${dot_content}  \"${root_name}\" -> \"${dep}\";\n"
    fi
  done
  # edges between G1 functions and LX dependencies
  for entry in "${dep_nodes[@]}"; do
    local dep=$(echo "$entry" | cut -d'|' -f3)
    local reliance_g1=$(echo "$entry" | cut -d'|' -f4)
    local reliance_lx=$(echo "$entry" | cut -d'|' -f5)

    local fx6a1=${dep%%[0-9]*}
    local fx6b1=${dep:${#fx6a1}}
    local fx6a2=${fx6b1%%[a-zA-Z]*}
    local fx6b2=${fx6b1:${#fx6a2}}
    local fx6a3=${fx6b2%%[0-9]*}
    local fx6b3=${fx6b2:${#fx6a3}}
    local fx6a4=${fx6b3%%[v]*}
    local fx6b4=${fx6b3:${#fx6a4}+1}
    local fx6a5=${fx6b4%%[.]*}
    local dep_node="f${fx6a2}${fx6a3}${fx6a4}v${fx6a5}"
    # edges to G1 functions
    local reliance_g1_ar=($reliance_g1)
    for sub_dep in "${reliance_g1_ar[@]}"; do
      local sfx6a1=${sub_dep%%[0-9]*}
      local sfx6b1=${sub_dep:${#sfx6a1}}
      local sfx6a2=${sfx6b1%%[a-zA-Z]*}
      local sfx6b2=${sfx6b1:${#sfx6a2}}
      local sfx6a3=${sfx6b2%%[0-9]*}
      local sfx6b3=${sfx6b2:${#sfx6a3}}
      local sfx6a4=${sfx6b3%%[v]*}
      local sfx6b4=${sfx6b3:${#sfx6a4}+1}
      local sfx6a5=${sfx6b4%%[.]*}
      local sub_dep_node="f${sfx6a2}${sfx6a3}${sfx6a4}v${sfx6a5}"
      dot_content="${dot_content}  \"${dep_node}\" -> \"${sub_dep_node}\";\n"
    done
    # edges to LX dependencies
    local reliance_lx_ar=($reliance_lx)
    for lx_dep in "${reliance_lx_ar[@]}"; do
      if [[ "$lx_dep" != "-" ]]; then
        dot_content="${dot_content}  \"${dep_node}\" -> \"${lx_dep}\";\n"
      fi
    done
  done
  dot_content="${dot_content}}"
}

[[ -z ${m0reliance} || ${m0sector} =~ ^# ]] && { exit 0; }

echo "$0: Processing ${m0type} ${m0sector} ${m0name} ${m0tier} ${m0version}"
collect_deps_ "$(echo ${m0reliance} | sed 's|,|\ |g')"
generate_dot_graph_ "${m0type}" "${m0sector}" "${m0name}" "${m0tier}" "${m0version}" "$(echo ${m0reliance} | sed 's|,|\ |g')"
mkdir -p "M${m0sector}"
tier0="${m0tier//+}"
if [[ "${m0type}" == "FCTN" ]]; then
  fname="f${m0sector}${m0name}${tier0}${m0version}"
else
  fname="a${m0sector}${m0name}${tier0}${m0version}"
fi
dot_filename="M${m0sector}/_${fname}.dot"
echo -e "$dot_content" > "$dot_filename"
echo "$0: Created $dot_filename"
if command -v dot >/dev/null 2>&1; then
  pdf_filename="M${m0sector}/_${fname}.pdf"
  dot -Tpdf "$dot_filename" -o "$pdf_filename"
  echo "$0: Created $pdf_filename"
else
  echo "$0: dot not installed, skipping PDF generation"
fi
